package net.siufung.mybatis.tenant;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
import net.siufung.core.exception.BizServiceException;

import java.util.List;

/**
 * MybatisPlusConfig
 * @author 陈建峰
 * @since 2022/3/29 3:37 下午
 */
@Slf4j
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class MybatisTenantLineInnerInterceptor extends TenantLineInnerInterceptor {

    private MybatisTenantLineHandler mybatisTenantLineHandler;

    public MybatisTenantLineInnerInterceptor(MybatisTenantLineHandler mybatisTenantLineHandler){
        this.mybatisTenantLineHandler = mybatisTenantLineHandler;
        super.setTenantLineHandler(mybatisTenantLineHandler);
    }

    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
        final Table table = insert.getTable();
        if (this.meetTenantConditions(table.getName())) {
            List<Column> columns = insert.getColumns();
            if (CollectionUtils.isNotEmpty(columns)) {
                String tenantIdColumn = mybatisTenantLineHandler.getTenantIdColumn();
                if (columns.stream().map(Column::getColumnName).anyMatch(i -> i.equals(tenantIdColumn))) {
                    return;
                }
                columns.add(new Column(tenantIdColumn));
                Select select = insert.getSelect();
                if (select != null) {
                    this.processInsertSelect(select.getSelectBody());
                } else if (insert.getItemsList() != null) {
                    ItemsList itemsList = insert.getItemsList();
                    Expression tenantId = mybatisTenantLineHandler.getTenantId();
                    if (itemsList instanceof MultiExpressionList) {
                        ((MultiExpressionList) itemsList).getExpressionLists()
                                .forEach(el -> el.getExpressions().add(tenantId));
                    } else {
                        ((ExpressionList) itemsList).getExpressions().add(tenantId);
                    }
                } else {
                    throw new BizServiceException(
                            "Failed to process multiple-table update, please exclude the tableName or statementId");
                }
            }
        }
    }

    /**
     * update 语句处理
     */
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        final Table table = update.getTable();
        if (this.meetTenantConditions(table.getName())) {
            update.setWhere(this.andExpression(table, update.getWhere()));
        }

    }

    /**
     * delete 语句处理
     */
    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        final Table table = delete.getTable();
        if (this.meetTenantConditions(table.getName())) {
            delete.setWhere(this.andExpression(table, delete.getWhere()));
        }
    }

    /**
     * 处理 PlainSelect
     */
    @Override
    protected void processPlainSelect(PlainSelect plainSelect) {
        FromItem fromItem = plainSelect.getFromItem();
        Expression where = plainSelect.getWhere();
        processWhereSubSelect(where);
        if (fromItem instanceof Table) {
            Table fromTable = (Table) fromItem;
            if (this.meetTenantConditions(fromTable.getName())) {
                plainSelect.setWhere(builderExpression(where, fromTable));
            }
        } else {
            processFromItem(fromItem);
        }
        List<Join> joins = plainSelect.getJoins();
        if (joins != null && joins.size() > 0) {
            joins.forEach(j -> {
                processJoin(j);
                processFromItem(j.getRightItem());
            });
        }
    }

    /**
     * 处理联接语句
     */
    @Override
    protected void processJoin(Join join) {
        if (join.getRightItem() instanceof Table) {
            Table fromTable = (Table) join.getRightItem();
            if (this.meetTenantConditions(fromTable.getName())) {
                join.addOnExpression(builderExpression(
                        Lists.newArrayList(join.getOnExpressions()).get(0), fromTable));
            }
        }
    }

    /**
     * 满足有效条件 才能使用租户
     * @return Boolean
     */
    private Boolean meetTenantConditions(String tableName){
        return !mybatisTenantLineHandler.ignoreTable(tableName) && mybatisTenantLineHandler.validConditions();
    }

}
